class Board {
  constructor(id, { width, height, offset = { x: 0, y: 0 } }) {
    const canvas = document.getElementById(id);
    canvas.width = width;
    canvas.height = height;
    this.offset = offset;
    this.canvas = canvas;
    const ctx = canvas.getContext('2d');
    this.ctx = ctx;
    this.draw = false;
    this.drawShape = null;
    this.renderList = [];
    this.tempDraw = {
      type: 'user_draw',
      id: '',
      coordinate: [],
    };
    this.userEvent = {};
    this.registerEvent();
  }

  registerUserEvent(type, callback) {
    if (!this.userEvent[type]) {
      this.userEvent[type] = [callback];
      return;
    }
    this.userEvent[type].push(callback);
  }

  registerEvent() {
    this.canvas.addEventListener('mousedown', this.handleMouseDown);
    this.canvas.addEventListener('mousemove', this.handleMouseMove);
    this.canvas.addEventListener('mouseup', this.handleMouseUp);
    this.canvas.addEventListener('contextmenu', this.handleContextMenu);
  }

  getPosition() {
    const { canvas, offset } = this;
    return {
      x: offset.x,
      y: offset.y,
      center: { x: canvas.width / 2 + offset.x, y: canvas.height / 2 - offset.y }
    };
  }

  handleContextMenu = (e) => {
    e.preventDefault();
    if (this.drawShape) {
      this.drawShape = null;
    }
  }

  handleMouseDown = (e) => {
    if (e.button === 2) {
      return;
    }
    const { offset, ctx, drawShape } = this;
    const x = e.clientX + offset.x;
    const y = e.clientY - offset.y;
    const coordinate = { x, y, start: true };
    if (!drawShape) {
      this.tempDraw.coordinate.push(coordinate);
      ctx.beginPath();
      ctx.moveTo(coordinate.x, coordinate.y);
    } else {
      const cloneShape = this.drawShape.clone();
      this.drawShape = cloneShape;
      cloneShape.onMouseDown({ x, y });
      this.renderList.push(cloneShape);
    }
    this.draw = true;
  }
  
  handleMouseMove = (e) => {
    const { draw, ctx, offset, drawShape } = this;
    if (!draw) return;
    const x = e.clientX + offset.x;
    const y = e.clientY - offset.y;
    const coordinate = { x, y, start: false };
    if (drawShape) {
      drawShape.onMouseMove({ x, y });
      this.render();
      return;
    }
    this.tempDraw.coordinate.push(coordinate);
    ctx.lineTo(coordinate.x, coordinate.y);
    ctx.stroke();
  }

  handleMouseUp = () => {
    this.draw = false;
    const { ctx, tempDraw, drawShape, userEvent } = this;
    if (!drawShape) {
      ctx.closePath();
      this.renderList.push({ ...tempDraw, id: Math.random() });
    }

    if (userEvent.mouseup) {
      for (let i = 0; i < userEvent.mouseup.length; i++) {
        const fn = userEvent.mouseup[i];
        fn();
      }
    }
  }

  addShape(shape) {
    if (shape.canDraw) {
      this.drawShape = shape;
    }
  }

  removeShape(id) {
    this.renderList = this.renderList.filter((v) => v.id !== id);
  }

  render() {
    const { ctx, renderList, canvas } = this;
    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
    renderList.forEach((v) => {
      const { coordinate, type } = v;
      if (type === 'user_draw') {
        ctx.beginPath();
        coordinate.forEach((v) => {
          if (v.start) {
            ctx.moveTo(v.x, v.y);
          } else {
            ctx.lineTo(v.x, v.y);
          }
        });
        ctx.stroke();
        ctx.closePath();
      } else {
        v.render(ctx, canvas);
      }
    });
  }
}